package eTaxi;

import java.io.*;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.MulticastSocket;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

import java.rmi.ConnectException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

import java.util.Hashtable;

public class User {

	static final int listen_port = 16666;

	static final int answer_port = 14444;

	static final int mcast_port = 19999;

	static InetAddress mcast_group;

	static MulticastSocket toTaxis;

	static int RMIport;

	static String bank_addr;

	static String cct;

	static E_Bank bank;

	static String name;

	static int credit;

	static UserProfile up;

	static UserProfile new_up;

	static String userIp;

	static InetAddress userAddr;

	static Socket toTaxi;

	static String taxi_host;

	static int taxi_port;

	static Hashtable banks;

    
    
    
	public static void main(String[] args) throws ConnectException {

		// inizializzazione del ciclo principale del main

		if (args.length != 1) {
			System.out.println("Errore nell'inserimento dei parametri!");
			return;
		}

		else {
			name = args[0];
		}

		try {

			userAddr = InetAddress.getLocalHost();

			userIp = userAddr.getHostAddress();

		} catch (UnknownHostException e) {
			System.out.println("Errore: host sconosciuto!");
			e.printStackTrace();
			return;

		}

		/*
		 * Se il metodo join restituisce false il main esce
		 *  
		 */

		if (!join()) {
			return;
		}

		String cmd;
		boolean quit = false;
		

		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		System.out.println(name + " benvenuto nel servizio E-Taxi!");

		/*
         * Ciclo principale del main(): si chiede di inserire un comando
		 */
        
		do {

			System.out
					.println("Inserire un comando : [registrazione] [chiama] [esci]");

			try {
				cmd = in.readLine();

				if (cmd == null)
					break;

				cmd.trim();

				if (cmd.equalsIgnoreCase(""))
					continue;

				else if (cmd.equalsIgnoreCase("chiama"))
					callTaxi();

				else if (cmd.equalsIgnoreCase("registrazione"))
					register();

				else if (cmd.equalsIgnoreCase("esci"))
					quit = true;

			} catch (IOException ioe) {
                System.out.println("Errore");
				System.out.println(ioe);
			}

		}

		while (!quit);

	}// fine main

	private static boolean join() {
		/*
		 * Il metodo join() unisce l'utente al gruppo multicast condiviso tra
		 * utenti e taxi dell'appilcazione
		 *  
		 */

		try {
			mcast_group = InetAddress.getByName("224.0.0.1");
			toTaxis = new MulticastSocket(mcast_port);
			toTaxis.joinGroup(mcast_group);  
            return true;
		}

		catch (IOException ioe) {
			System.out.println("Problemi nel collegamento ai Taxi!");

			return false;
		}

	}

	private static boolean setup() {

		/*
		 * Questo metodo gestisce la fase di lookup al servizio remoto richiesto
		 * per il funzionamento dell'applicazione. In caso di esito negativo
		 * restituisce false.
		 *  
		 */

		try {
			System.out.println("Collegamento in corso con la banca");

			System.out
					.println(".........................................................");
			bank = (E_Bank) Naming.lookup("//" + bank_addr + ":" + RMIport
					+ "/bank");

			System.out.println("Collegamento con la banca effettuato.");
			return true;

		}

		catch (MalformedURLException mue) {

			System.out.println("Errore nel servizio remoto della banca");
			System.out.println("Collegamento con la banca non terminato");
			System.out.println(mue);
			return false;
		}

		catch (RemoteException re) {
			System.out.println("Errore nel servizio remoto della banca");
			System.out.println("Collegamento con la banca non terminato");
			System.out.println(re);
			return false;

		}

		catch (NotBoundException nbe) {
			System.out.println("Errore nel servizio remoto della banca");
			System.out.println("Collegamento con la banca non terminato");
			System.out.println(nbe);
			return false;

		}

	}

	private static void register() {

		/*
		 * Il metodo register() provvede a registrare l'utente all'applicazione
		 * E-Taxi, eseguendo il metodo remoto regE_Taxi.
         * Se la banca nega la registrazione del profilo utente, questo non viene cambiato
         * 
		 *  
		 */

		BufferedReader console = new BufferedReader(new InputStreamReader(
				System.in));

		try {
			System.out.println("Inserire l'indirizzo della banca");

			bank_addr = console.readLine();

			
			System.out.println("Inserire la porta RMI della banca");

			RMIport = new Integer(console.readLine()).intValue();

			System.out
					.println("Inserire il credito da versare nel conto della banca");

			credit = new Integer(console.readLine()).intValue();

		} catch (NumberFormatException nfe) {
			System.out.println(nfe);
			System.out.println("Errore nell'inserimento dei parametri!");
		}

		catch (IOException ioe) {
			System.out.println(ioe);
			System.out.println("Errore nell'inserimento dei parametri!");
			return;

		}

		
		/*
		 * Per registrarsi bisogna prima effettuare un collegamento al servizio
		 * RMI offerto dal server della banca; se il collegamento non viene
		 * portato a termine il metodo register() non modifica l'attuale
		 * UserProfile.
		 */

		if (!setup()) {
			return;
		}

		else {

			int reply;

			System.out.println("Registrazione in corso....");

			cct = bank_addr + ":" + RMIport;
			UserProfile new_up = new UserProfile(name, userIp, answer_port,
					cct, credit);
			System.out.println("name " + name);
			System.out.println("userIp " + userIp);
			System.out.println("answer_port " + answer_port);
			System.out.println("cct " + cct);

			try {
				reply = bank.regE_Taxi(new_up);

			}

			catch (RemoteException re) {
				System.out
						.println("Errore nella procedura di registrazione in remoto con la banca");
				System.out.println("Registrazione al servizio E-Taxi fallita");
				System.out.println(re);
				return;
			}

			if (reply == 0) { 
				System.out
						.println("Registrazione al servizio E-Taxi fallita: si è già registrati presso questa banca!");
				return;
			}

			else {
				up = new_up;
				
				System.out.println("Registrazione presso la banca effettuata.");
				System.out.println("L'utente " + name
						+ " ?? registrato presso la banca " + cct);

				return;
			}
		}
	}

	private static void callTaxi() {

		if (up == null) {
			System.out
					.println("Impossibile chiamare alcun taxi, nessuna banca scelezionata!!");
			return;
		}

		DatagramSocket handshake;
		DatagramPacket ack;
		String aux;

		String call = "REQ " + up.name + " " + up.address + " " + up.port + " \n";
		System.out.println("Ho inviato il messaggio: " + call);

		byte[] msg = call.getBytes();
		DatagramPacket req = new DatagramPacket(msg, msg.length, mcast_group,
				mcast_port);

		try {
			toTaxis.send(req);

		}

		catch (IOException ioe) {

		}
		String[] rcvdmsg;

		int taxi_port = 0; // 0 temporaneo
		int ridetype;
		String taxi_host = null; // null temporaneo

		String ride;

		Socket toTaxi = null;
		DataOutputStream out = null;

		ServerSocket transaction_s = null;
		Socket transaction = null;
		BufferedReader transaction_br;

		try {
			handshake = new DatagramSocket(answer_port, userAddr); 
			
			handshake.setSoTimeout(4000);

			ack = new DatagramPacket(new byte[1000], 1000);
			boolean answer = false;
			while (!answer) {

				System.out.println("Taxi!!!!");
				toTaxis.send(req);
				System.out
						.println("Sono in attesa di ricevere una risposta da parte di un Taxi");

				try {
					handshake.receive(ack);
					handshake.close();
					aux = new String(ack.getData());
					String[] ackmsg = aux.split("\n");
					System.out
							.println("Ho ricevuto da un Taxi la seguente risposta: "
									+ ackmsg[0]);
					rcvdmsg = ackmsg[0].split(" ");

					taxi_host = rcvdmsg[3];

					taxi_port = new Integer(rcvdmsg[4]).intValue();
					System.out.println("Finalmente un Taxi...");
					answer = true;

				}

				catch (SocketTimeoutException ste) {
					
					System.out
							.println("Nessun Taxi ha risposto alla chiamata...");
					answer = false;

				}

			}// fine while

			double d = Math.random();
			ridetype = 1;
			if (d > 0.3 && d < 0.6) {
				ridetype = 2;
			} else {
				if (d >= 0.6)
					ridetype = 3;
			}
			
            
			ride = "RES " + up.name + " " + up.address + " " + listen_port + " "
					+ ridetype + " " + cct + " \n";

			transaction_s = new ServerSocket(listen_port);

			// collegamento al serversocket del taxi
			toTaxi = new Socket(taxi_host, taxi_port);
			transaction_s.setSoTimeout(4000);
			out = new DataOutputStream(toTaxi.getOutputStream());

			out.flush();
			out.writeBytes(ride);
			System.out.println("Ho inviato i dati del mio profilo al Taxi.");

			// creazione del socket sulla porta 6666

			transaction = transaction_s.accept();

			System.out.println("Attendo una risposta da parte del Taxi....");

			transaction_br = new BufferedReader(new InputStreamReader(
					transaction.getInputStream()));

			String transaction_msg = transaction_br.readLine();

			System.out.println("Il Taxi ha risposto: " + transaction_msg);

			String[] feedback = transaction_msg.split(" ");

			if (feedback[1].compareTo("FAIL") == 0)
				System.out.println("Il Taxi ha rifiutato la chiamata!");

            
			
			else if (feedback[1].compareTo("OK") == 0){
				System.out.println("Il Taxi ha accettato la chiamata!");
				String ride_details = transaction_br.readLine();
				System.out.println("RIDE DETAILS: " + ride_details);
				String[] payment = ride_details.split(" ");

				int ride_cost = new Integer(payment[1]).intValue();
				int ride_number = new Integer(payment[2]).intValue();

				int pay_response = bank.payRide(ride_cost, ride_number);

				if (pay_response == 0) {

					System.out
							.println("Errore nel pagamento...contattare la banca!");

				} else {

					while (bank.notify(ride_number) == 0) {
						try {
							System.out
									.println("Il Cliente sta aspettando una risposta dalla banca");
							Thread.sleep(500);
						} catch (InterruptedException e) {
						}
					}
					System.out.println("NOTIFY OK");

				}

			}// fine else pagamento acettato

            else {
                System.out.println("Errore..ho ricevuto un messaggio non conforme!");
            }
            
            
			transaction_s.close();
			toTaxi.close();
			//handshake.close();

		}// fine try

		catch (SocketTimeoutException ste) {
			
			System.out.println("Timeout nelle comunicazioni con il taxi...");
			System.out.println(ste);
			try {
				transaction_s.close();
				toTaxi.close();
				//handshake.close();
			}

			catch (IOException ioe) {

				System.out.println(ioe);
				ioe.printStackTrace();

			}

		} catch (IOException ioe) {

			System.out.println(ioe);
			ioe.printStackTrace();
		}

	}// fine calltaxi

	

} // fine User
